package ChannelTutorial.Demo05;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;

/**
 * Demo05 - Channel 常用操作实战 - 客户端
 * 
 * 学习目标：
 * 1. 掌握 Channel 的读写操作（write, writeAndFlush, read）
 * 2. 了解 Channel 的配置（ChannelConfig）
 * 3. 学习 Channel 的状态查询方法
 * 4. 掌握 Channel 的地址操作
 * 5. 学习 Channel 的关闭操作
 * 6. 了解 Channel 的可写状态管理
 */
@Slf4j
public class Client {

    public static void main(String[] args) throws Exception {
        log.info("=== Channel 常用操作实战 - 客户端 ===\n");
        
        // 演示 1：读写操作
        log.info("\n========================================");
        log.info("演示 1: Channel 读写操作");
        log.info("========================================\n");
        demo1_ReadWriteOperations();
        
        Thread.sleep(2000);
        
        // 演示 2：Channel 配置
        log.info("\n========================================");
        log.info("演示 2: Channel 配置");
        log.info("========================================\n");
        demo2_ChannelConfig();
        
        Thread.sleep(2000);
        
        // 演示 3：状态查询
        log.info("\n========================================");
        log.info("演示 3: Channel 状态查询");
        log.info("========================================\n");
        demo3_StateQuery();
        
        Thread.sleep(2000);
        
        // 演示 4：地址操作
        log.info("\n========================================");
        log.info("演示 4: Channel 地址操作");
        log.info("========================================\n");
        demo4_AddressOperations();
        
        Thread.sleep(2000);
        
        // 演示 5：可写状态管理
        log.info("\n========================================");
        log.info("演示 5: Channel 可写状态管理");
        log.info("========================================\n");
        demo5_WritableState();
        
        Thread.sleep(2000);
        
        // 演示 6：关闭操作
        log.info("\n========================================");
        log.info("演示 6: Channel 关闭操作");
        log.info("========================================\n");
        demo6_CloseOperations();
        
        Thread.sleep(2000);
        System.exit(0);
    }

    /**
     * 演示 1：Channel 读写操作
     * 
     * 关键方法：
     * - write(Object msg)：写入数据到缓冲区
     * - flush()：刷新缓冲区，发送数据
     * - writeAndFlush(Object msg)：写入并立即发送
     * - read()：从 Channel 读取数据
     */
    private static void demo1_ReadWriteOperations() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ch.pipeline().addLast(new SimpleClientHandler());
                    }
                });
            
            Channel channel = bootstrap.connect("localhost", 8080).sync().channel();
            
            // 1. write() - 只写入缓冲区，不发送
            log.info("1. 使用 write() 写入数据（不立即发送）");
            channel.write(Unpooled.copiedBuffer("Message 1\n", CharsetUtil.UTF_8));
            channel.write(Unpooled.copiedBuffer("Message 2\n", CharsetUtil.UTF_8));
            channel.write(Unpooled.copiedBuffer("Message 3\n", CharsetUtil.UTF_8));
            log.info("   已写入 3 条消息到缓冲区");
            
            Thread.sleep(500);
            
            // 2. flush() - 刷新缓冲区，发送所有数据
            log.info("\n2. 使用 flush() 发送所有缓冲的数据");
            channel.flush();
            log.info("   已发送所有缓冲的消息");
            
            Thread.sleep(500);
            
            // 3. writeAndFlush() - 写入并立即发送（推荐）
            log.info("\n3. 使用 writeAndFlush() 写入并立即发送");
            channel.writeAndFlush(
                Unpooled.copiedBuffer("Immediate message\n", CharsetUtil.UTF_8)
            );
            log.info("   已立即发送消息");
            
            Thread.sleep(500);
            
            // 4. 批量写入
            log.info("\n4. 批量写入优化");
            for (int i = 0; i < 5; i++) {
                if (i < 4) {
                    // 前 4 条只写入缓冲区
                    channel.write(
                        Unpooled.copiedBuffer("Batch " + i + "\n", CharsetUtil.UTF_8)
                    );
                } else {
                    // 最后一条写入并刷新
                    channel.writeAndFlush(
                        Unpooled.copiedBuffer("Batch " + i + "\n", CharsetUtil.UTF_8)
                    );
                }
            }
            log.info("   批量发送 5 条消息");
            
            Thread.sleep(1000);
            channel.close().sync();
            
        } finally {
            group.shutdownGracefully();
        }
    }

    /**
     * 演示 2：Channel 配置
     * 
     * ChannelConfig 提供了丰富的配置选项
     */
    private static void demo2_ChannelConfig() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ChannelConfig config = ch.config();
                        
                        log.info("┌─────────────────────────────────────────");
                        log.info("│ Channel 配置信息：");
                        log.info("│");
                        
                        // 1. 连接超时
                        log.info("│ 连接超时: {} ms", 
                            config.getConnectTimeoutMillis());
                        config.setConnectTimeoutMillis(5000); // 设置为 5 秒
                        log.info("│ 设置连接超时为: 5000 ms");
                        
                        // 2. 写缓冲区水位
                        log.info("│");
                        log.info("│ 写缓冲区高水位: {} bytes", 
                            config.getWriteBufferHighWaterMark());
                        log.info("│ 写缓冲区低水位: {} bytes", 
                            config.getWriteBufferLowWaterMark());
                        
                        // 设置水位（64KB - 32KB）
                        config.setWriteBufferWaterMark(
                            new WriteBufferWaterMark(32 * 1024, 64 * 1024)
                        );
                        log.info("│ 已设置水位: 低=32KB, 高=64KB");
                        
                        // 3. 自动读取
                        log.info("│");
                        log.info("│ 自动读取: {}", config.isAutoRead());
                        // config.setAutoRead(false); // 禁用自动读取
                        
                        // 4. 接收缓冲区分配器
                        log.info("│");
                        log.info("│ 接收缓冲区分配器: {}", 
                            config.getRecvByteBufAllocator().getClass().getSimpleName());
                        
                        // 5. ByteBuf 分配器
                        log.info("│ ByteBuf 分配器: {}", 
                            config.getAllocator().getClass().getSimpleName());
                        
                        // 6. 消息大小估算器
                        log.info("│ 消息大小估算器: {}", 
                            config.getMessageSizeEstimator().getClass().getSimpleName());
                        
                        log.info("│");
                        log.info("└─────────────────────────────────────────\n");
                        
                        ch.pipeline().addLast(new SimpleClientHandler());
                    }
                });
            
            Channel channel = bootstrap.connect("localhost", 8080).sync().channel();
            
            channel.writeAndFlush(
                Unpooled.copiedBuffer("Testing config\n", CharsetUtil.UTF_8)
            );
            
            Thread.sleep(1000);
            channel.close().sync();
            
        } finally {
            group.shutdownGracefully();
        }
    }

    /**
     * 演示 3：Channel 状态查询
     * 
     * Channel 提供了多个状态查询方法
     */
    private static void demo3_StateQuery() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ch.pipeline().addLast(new SimpleClientHandler());
                    }
                });
            
            log.info("1. 连接前的状态：");
            ChannelFuture connectFuture = bootstrap.connect("localhost", 8080);
            Channel channel = connectFuture.channel();
            printChannelState(channel);
            
            connectFuture.sync();
            
            log.info("\n2. 连接后的状态：");
            printChannelState(channel);
            
            // 发送数据
            channel.writeAndFlush(
                Unpooled.copiedBuffer("State test\n", CharsetUtil.UTF_8)
            );
            
            Thread.sleep(500);
            
            log.info("\n3. 发送数据后的状态：");
            printChannelState(channel);
            
            // 关闭连接
            channel.close().sync();
            
            log.info("\n4. 关闭后的状态：");
            printChannelState(channel);
            
        } finally {
            group.shutdownGracefully();
        }
    }

    /**
     * 打印 Channel 状态
     */
    private static void printChannelState(Channel channel) {
        log.info("   isOpen: {}      - Channel 是否打开", channel.isOpen());
        log.info("   isActive: {}    - Channel 是否激活（已连接）", channel.isActive());
        log.info("   isRegistered: {} - Channel 是否注册到 EventLoop", channel.isRegistered());
        log.info("   isWritable: {}  - Channel 是否可写", channel.isWritable());
    }

    /**
     * 演示 4：Channel 地址操作
     */
    private static void demo4_AddressOperations() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ch.pipeline().addLast(new SimpleClientHandler());
                    }
                });
            
            Channel channel = bootstrap.connect("localhost", 8080).sync().channel();
            
            log.info("┌─────────────────────────────────────────");
            log.info("│ Channel 地址信息：");
            log.info("│");
            
            // 1. 本地地址
            InetSocketAddress localAddress = 
                (InetSocketAddress) channel.localAddress();
            log.info("│ 本地地址: {}", localAddress);
            log.info("│   主机: {}", localAddress.getHostString());
            log.info("│   端口: {}", localAddress.getPort());
            
            // 2. 远程地址
            log.info("│");
            InetSocketAddress remoteAddress = 
                (InetSocketAddress) channel.remoteAddress();
            log.info("│ 远程地址: {}", remoteAddress);
            log.info("│   主机: {}", remoteAddress.getHostString());
            log.info("│   端口: {}", remoteAddress.getPort());
            
            // 3. Channel ID
            log.info("│");
            log.info("│ Channel ID (短): {}", channel.id().asShortText());
            log.info("│ Channel ID (长): {}", channel.id().asLongText());
            
            log.info("│");
            log.info("└─────────────────────────────────────────\n");
            
            channel.writeAndFlush(
                Unpooled.copiedBuffer("Address test\n", CharsetUtil.UTF_8)
            );
            
            Thread.sleep(1000);
            channel.close().sync();
            
        } finally {
            group.shutdownGracefully();
        }
    }

    /**
     * 演示 5：Channel 可写状态管理
     * 
     * 当写缓冲区达到高水位时，isWritable() 返回 false
     * 当写缓冲区降到低水位时，isWritable() 返回 true
     */
    private static void demo5_WritableState() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        // 设置较小的水位，便于演示
                        ch.config().setWriteBufferWaterMark(
                            new WriteBufferWaterMark(512, 1024)
                        );
                        
                        ch.pipeline().addLast(new WritableStateHandler());
                        ch.pipeline().addLast(new SimpleClientHandler());
                    }
                });
            
            Channel channel = bootstrap.connect("localhost", 8080).sync().channel();
            
            log.info("开始快速发送大量数据...\n");
            
            // 快速发送大量数据，触发不可写状态
            for (int i = 0; i < 100; i++) {
                // Java 8 兼容：使用循环代替 repeat()
                StringBuilder padding = new StringBuilder();
                for (int j = 0; j < 100; j++) {
                    padding.append("x");
                }
                ByteBuf buf = Unpooled.copiedBuffer(
                    "Large message " + i + " " + padding.toString() + "\n",
                    CharsetUtil.UTF_8
                );
                
                channel.write(buf);
                
                if (!channel.isWritable()) {
                    log.warn("⚠️  Channel 不可写！暂停发送...");
                    channel.flush();
                    break;
                }
            }
            
            channel.flush();
            
            Thread.sleep(2000);
            channel.close().sync();
            
        } finally {
            group.shutdownGracefully();
        }
    }

    /**
     * 演示 6：Channel 关闭操作
     */
    private static void demo6_CloseOperations() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ch.pipeline().addLast(new SimpleClientHandler());
                    }
                });
            
            // 方式 1：close() - 返回 ChannelFuture
            log.info("方式 1: 使用 close() 关闭");
            Channel channel1 = bootstrap.connect("localhost", 8080).sync().channel();
            
            ChannelFuture closeFuture = channel1.close();
            closeFuture.addListener((ChannelFutureListener) future -> {
                if (future.isSuccess()) {
                    log.info("✅ Channel 已成功关闭");
                }
            });
            closeFuture.sync();
            
            Thread.sleep(500);
            
            // 方式 2：使用监听器在发送完成后关闭
            log.info("\n方式 2: 发送完成后自动关闭");
            Channel channel2 = bootstrap.connect("localhost", 8080).sync().channel();
            
            channel2.writeAndFlush(
                Unpooled.copiedBuffer("Final message\n", CharsetUtil.UTF_8)
            ).addListener(ChannelFutureListener.CLOSE);
            
            log.info("✅ 消息发送完成后会自动关闭");
            
            Thread.sleep(500);
            
            // 方式 3：监听 closeFuture
            log.info("\n方式 3: 监听 closeFuture");
            Channel channel3 = bootstrap.connect("localhost", 8080).sync().channel();
            
            channel3.closeFuture().addListener((ChannelFutureListener) future -> {
                log.info("✅ 检测到 Channel 已关闭，执行清理工作");
            });
            
            channel3.writeAndFlush(
                Unpooled.copiedBuffer("Test\n", CharsetUtil.UTF_8)
            );
            
            Thread.sleep(500);
            channel3.close().sync();
            
        } finally {
            group.shutdownGracefully();
        }
    }

    /**
     * 简单的客户端处理器
     */
    @Slf4j
    static class SimpleClientHandler extends ChannelInboundHandlerAdapter {
        
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            ByteBuf buf = (ByteBuf) msg;
            String data = buf.toString(CharsetUtil.UTF_8);
            log.info("[客户端] 收到回复: {}", data.trim());
            buf.release();
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            ctx.close();
        }
    }

    /**
     * 可写状态处理器
     */
    @Slf4j
    static class WritableStateHandler extends ChannelInboundHandlerAdapter {
        
        @Override
        public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            
            if (channel.isWritable()) {
                log.info("✅ Channel 可写状态恢复");
            } else {
                log.warn("⚠️  Channel 不可写（写缓冲区已满）");
            }
            
            ctx.fireChannelWritabilityChanged();
        }
    }
}

/*
=== 运行结果分析 ===

1. 读写操作：

   write()：
   - 只写入缓冲区，不立即发送
   - 适合批量写入场景
   - 需要配合 flush() 使用

   flush()：
   - 刷新缓冲区，发送所有待发送的数据
   - 通常在批量 write() 后调用

   writeAndFlush()：
   - 写入并立即发送
   - 最常用的方式
   - 等同于 write() + flush()

2. Channel 配置：

   重要配置项：
   - connectTimeoutMillis：连接超时时间
   - writeBufferWaterMark：写缓冲区水位
   - autoRead：是否自动读取
   - recvByteBufAllocator：接收缓冲区分配器
   - allocator：ByteBuf 分配器

3. 状态查询：

   isOpen()：Channel 是否打开
   - 创建后即为 true
   - 关闭后为 false

   isActive()：Channel 是否激活
   - 连接建立后为 true
   - 连接断开后为 false

   isRegistered()：是否注册到 EventLoop
   - 注册后为 true

   isWritable()：是否可写
   - 写缓冲区未满时为 true
   - 达到高水位时为 false

4. 地址操作：

   localAddress()：本地地址
   remoteAddress()：远程地址
   id()：Channel 唯一标识

5. 可写状态管理：

   - 写缓冲区有高低水位
   - 达到高水位时 isWritable() = false
   - 降到低水位时 isWritable() = true
   - 监听 channelWritabilityChanged 事件

6. 关闭操作：

   close()：返回 ChannelFuture
   closeFuture()：获取关闭的 Future
   CLOSE 监听器：发送完成后自动关闭

=== 最佳实践 ===

✅ 批量写入时使用 write() + flush()
✅ 单条消息使用 writeAndFlush()
✅ 根据业务需求调整配置
✅ 监听 channelWritabilityChanged 控制发送速度
✅ 发送完成后使用 CLOSE 监听器关闭

❌ 不要频繁调用 flush()
❌ 不要忽略 isWritable() 状态
❌ 不要在不可写时继续写入大量数据
❌ 不要忘记释放 ByteBuf
❌ 不要在已关闭的 Channel 上操作
*/

